package com.rgp.serviceorder.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.rgp.internalcommon.common.*;
import com.rgp.internalcommon.dto.*;
import com.rgp.internalcommon.response.*;
import com.rgp.serviceorder.feign.ServiceDriverUserClient;
import com.rgp.serviceorder.feign.ServiceMapClient;
import com.rgp.serviceorder.feign.ServicePriceClient;
import com.rgp.serviceorder.feign.ServiceSsePushClient;
import com.rgp.serviceorder.mapper.OrderMapper;
import com.rgp.serviceorder.pojo.Order;
import com.rgp.serviceorder.service.OrderService;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author nhp
 * @version 1.0
 * @description TODO
 * @date 2024-04-20 5:37
 */
@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private ServicePriceClient servicePriceClient;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private ServiceDriverUserClient serviceDriverUserClient;

    @Autowired
    private ServiceMapClient serviceMapClient;

    @Autowired
    private RedissonClient redissonClient;

    @Autowired
    private ServiceSsePushClient serviceSsePushClient;

    @Override
    public ResponseResult addOrder(OrderDto orderDto) {

        // 1.校验下单时获取的计价规则的版本 与 预估价格时获取的计价规则的版本是否一致[本质是校验下订单时的价格 与预估价格是否一致]
        String fareType = orderDto.getFareType();
        Integer oldFareVersion = orderDto.getFareVersion();
        FareTypeDto fareTypeDto = new FareTypeDto();
        fareTypeDto.setFareType(fareType);
        ResponseResult<FareVersionResponse> newestFareVersion = servicePriceClient.getNewestFareVersion(fareTypeDto);
        Integer fareVersion = newestFareVersion.getData().getFareVersion();
        if (!fareVersion.equals(oldFareVersion)){
            return ResponseResult.fail(CommonStatusEnum.TOLL_RULE_VERSION_CHANGE_ERROR);
        }

        // 2.校验当前乘客是否可以下单
        // 2.1当前乘客是否存在未支付的订单 或者 当前有正在派单的订单
        QueryWrapper<Order> wrapper = new QueryWrapper<>();
        wrapper.eq("passenger_phone",orderDto.getPassengerPhone());
        List<Order> orderList = orderMapper.selectList(wrapper);

        // 校验乘客是否存在未支付的订单
        List<Order> unPayOrders = orderList.stream().filter(order -> (
                order.getOrderStatus().equals(OrderConstant.START_PAYMENT_STATUS))
        ).collect(Collectors.toList());
        if (unPayOrders != null && unPayOrders.size() > 0){
            return ResponseResult.fail(CommonStatusEnum.EXIST_UNPAY_ORDER_ERROR);
        }
        // 校验乘客是否存在正在派单中的订单
        List<Order> orders = orderList.stream().filter(order -> (
                        order.getOrderStatus().equals(OrderConstant.START_STATUS)
                        || order.getOrderStatus().equals(OrderConstant.RECEIVE_ORDER_STATUS)
                        || order.getOrderStatus().equals(OrderConstant.TO_PICK_UP_STATUS)
                        || order.getOrderStatus().equals(OrderConstant.DRIVER_ARRIVE_DEPARTURE_STATUS)
                        || order.getOrderStatus().equals(OrderConstant.PASSENGER_GETON_STATUS)
                        || order.getOrderStatus().equals(OrderConstant.PASSENGER_GETOFF_STATUS)))
                .collect(Collectors.toList());
        if (orders != null && orders.size() > 0){
            return ResponseResult.fail(CommonStatusEnum.EXIST_ONGOING_ORDER_ERROR);
        }
        // 2.2校验当前乘客是否是黑名单用户
        // 思考：这里是否需要考虑redis在此执行指令的原子性？
        // 答：不需要。下订单这件事虽然是存在并发行为，但是下单对于每位乘客而言，并不是并发行为，
        // 即此处并不存在临界资源的争抢，所以不需要考虑redis指令的原子性
        String deviceCode = orderDto.getDeviceCode();
        String passengerDeviceCode = RedisPrefixConstant.BLACK_DEVICE_PASSENGER_PREFIX + deviceCode;
        Boolean aBoolean = stringRedisTemplate.hasKey(passengerDeviceCode);
        if (aBoolean){
            String count = stringRedisTemplate.opsForValue().get(passengerDeviceCode);
            int i = Integer.parseInt(count);
            if ( i > 2){
                return ResponseResult.fail(CommonStatusEnum.DEVICE_IS_BLACK_ERROR);
            }else{
                stringRedisTemplate.opsForValue().increment(passengerDeviceCode);
            }
        }else{
            stringRedisTemplate.opsForValue().set(passengerDeviceCode,"1",1, TimeUnit.HOURS);
        }

        // 3.业务逻辑校验
        // 3.1当前城市是否开通了相应车型的服务、当前城市与车型是否存在对应的计价规则
        FareTypeDto dto = new FareTypeDto();
        dto.setFareType(orderDto.getFareType());
        ResponseResult<Boolean> booleanResponseResult = servicePriceClient.ifExists(dto);
        if (!booleanResponseResult.getData()){
            return ResponseResult.fail(CommonStatusEnum.ONLINE_TAXI_SERVICE_NOT_EXIST_ERROR);
        }

        //3.2 查询当前城市是否存在司机
        String cityCode = orderDto.getAddress();
        ResponseResult<Boolean> responseResult = serviceDriverUserClient.judgeIfExistAvailableDrivers(cityCode, DriverInfoConstant.VALID_STATE);
        if (!responseResult.getData()){
            return ResponseResult.fail(CommonStatusEnum.ONLINE_TAXI_SERVICE_NOT_EXIST_ERROR);
        }

        Order order = new Order();
        //订单属性赋值[城市编码、下单时间、预约时间、起点、终点、加密方式、运价类型编码]
        BeanUtils.copyProperties(orderDto,order);
        order.setOrderTime(new Date());
        order.setOrderStatus(OrderConstant.START_STATUS);
        // 4.插入订单
        int insert = orderMapper.insert(order);
        if (insert != 1){
            return ResponseResult.fail(CommonStatusEnum.FAIL_TO_CREATE_ORDER_ERROR);
        }
        //5.在乘客起点附近进行周边搜索、派单
        for (int i = 0;i < AroundSearchConstant.AROUND_SEARCH_FREQUENCY;i++) {
            Integer update = this.dispatcherOrder(orderDto, order);
            if (update == null) {
                if (i == AroundSearchConstant.AROUND_SEARCH_FREQUENCY){
                    return ResponseResult.success(CommonStatusEnum.NO_DRIVER_TO_DISPATCHER_ERROR);
                }
                try {
                    //等待20秒后再进行周边搜索
                    Thread.sleep(AroundSearchConstant.AROUND_SEARCH_WAIT_TIME);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                continue;
            } else if (!update.equals(1)) {
                return ResponseResult.fail(CommonStatusEnum.FAIL_TO_UPDATE_ORDER_ERROR);
            }else {
                break;
            }
        }

        return ResponseResult.success();
    }

    public Integer dispatcherOrder(OrderDto orderDto,Order order){
        String depLatitude = orderDto.getDepLatitude();
        String depLongitude = orderDto.getDepLongitude();
        String center = depLatitude + "," + depLongitude;
        AroundSearchDto aroundSearchDto = new AroundSearchDto();
        aroundSearchDto.setCenter(center);
        List<Integer> list = new ArrayList<>();
        list.add(2000);
        list.add(4000);
        list.add(5000);
        ArrayList<String> carIdList = new ArrayList<>();
        radius:
        for (int i = 0;i < list.size();i++){
            aroundSearchDto.setRadius(list.get(i));
            // 周边搜索得到乘客起点附近的终端【车辆id】
            ResponseResult<AroundSearchResponse[]> aroundSearchResponse = serviceMapClient.aroundSearch(aroundSearchDto);
            AroundSearchResponse[] data = aroundSearchResponse.getData();
            for (AroundSearchResponse item : data) {
                carIdList.add(item.getCarId());//车辆id
            }
            // 根据车辆id查询其对应的司机
            List<DriverCarResponse> driverCarResponses = serviceDriverUserClient.getDriversByCarIds(carIdList).getData();

            //过滤司机，过滤条件1：司机对应的载具类型需要符合乘客所选
            driverCarResponses = driverCarResponses.stream().
                    filter(item -> !item.getVehicleType().equals(orderDto.getVehicleType())).
                    collect(Collectors.toList());

            List<Long> driverIds = new ArrayList<>();
            driverCarResponses.stream().forEach(item->driverIds.add(item.getDriverId()));
            // 过滤司机，过滤条件2：司机当前处于出车状态
            List<DriverStatus> status = serviceDriverUserClient.getStatusByDriverIds(driverIds).getData().getStatus();
            for (DriverStatus item : status){
                if (item.getStatus() != DriverWorkStatusConstant.START){
                    driverIds.remove(item.getDriverId());
                }
            }
            // 进一步过滤司机，过滤条件3：司机当前没有正在进行中的订单
            List<Long> unstatifiedDriverIds = orderMapper.selectDriversByDriverIdAndOrderStatus(driverIds);
            unstatifiedDriverIds.stream().forEach(item->driverIds.remove(item));

            if (driverIds.size() == 0){
                continue;
            }
            RLock lock = null;
            for (int num = 0;num < driverIds.size();num++){
                Long driverId = driverIds.get(num);
                lock = redissonClient.getLock(RedisPrefixConstant.DRIVER_USER_ORDER_LOCK_PREFIX + driverId);
                //尝试加锁
                boolean lockResult = lock.tryLock();
                if (lockResult){
                    break;
                }
                if (num == driverIds.size() && !lockResult){
                    continue radius;
                }
            }

            // 此时，乘客可以分配到司机，接下来的工作是：订单匹配司机【获取相应的订单数据填充到到订单中】
            Long driverId = driverIds.get(0);
            // 1.根据司机id获取司机信息
            DriverInfoResponse driverInfoByDriverId = serviceDriverUserClient.getDriverInfoByDriverId(driverId).getData();
            // 司机手机号
            String driverPhone = driverInfoByDriverId.getDriverPhone();
            // 司机驾照
            String licenseId = driverInfoByDriverId.getLicenseId();
            List<DriverCarResponse> collect = driverCarResponses.stream().filter(item -> item.getDriverId().equals(driverId)).collect(Collectors.toList());
            // 2.车辆id
            Long carId = collect.get(0).getCarId();
            // 3.车牌号
            String vehicleNo = collect.get(0).getVehicleNo();
            // 4.收到订单时，司机的经纬度
            List<AroundSearchResponse> receiveOrderAddress = Arrays.stream(data).filter(item -> !item.getCarId().equals(carId)).collect(Collectors.toList());
            AroundSearchResponse searchResponse = receiveOrderAddress.get(0);
            Double longitude = searchResponse.getLongitude();
            Double latitude = searchResponse.getLatitude();

            // 向订单中新插入以下内容
            order.setDriverId(driverId);
            order.setDriverPhone(driverPhone);
            order.setLicenseId(licenseId);
            order.setCarId(carId);
            order.setVehicleNo(vehicleNo);
            order.setReceiveOrderTime(new Date());
            order.setReceiveOrderCarLatitude(latitude + "");
            order.setReceiveOrderCarLongitude(longitude + "");
            order.setOrderStatus(OrderConstant.RECEIVE_ORDER_STATUS);
            int update = orderMapper.updateById(order);
            //给司机推送消息
            Map<String, String> passengerMap = new HashMap<>();
            passengerMap.put("passengerId",orderDto.getPassengerId() + "");
            passengerMap.put("passengerPhone",orderDto.getPassengerPhone());
            passengerMap.put("departure",orderDto.getDeparture());
            passengerMap.put("depLongitude",orderDto.getDepLongitude());
            passengerMap.put("depLatitude",orderDto.getDepLatitude());
            passengerMap.put("destination",orderDto.getDestination());
            passengerMap.put("destLongitude",orderDto.getDepLongitude());
            passengerMap.put("destLatitude",orderDto.getDepLatitude());
            String passengerJson = JSON.toJSONString(passengerMap);
            serviceSsePushClient.push(driverId + "",passengerJson);
            //向乘客推送消息
            JSONObject driverObject = new JSONObject();
            driverObject.put("driverPhone",driverPhone);
            driverObject.put("vehicleNo",vehicleNo);
            driverObject.put("receiveOrderCarLatitude",latitude);
            driverObject.put("receiveOrderCarLongitude",longitude);
            //查询车辆信息
            LinkedHashMap<String, Object> car = (LinkedHashMap<String, Object>) serviceDriverUserClient.selectCarById(carId).getData();
            String brand = (String) car.get("brand");
            String model = (String) car.get("model");
            driverObject.put("brand",brand);
            driverObject.put("model",model);
            String driverJson = driverObject.toString();
            serviceSsePushClient.push(orderDto.getPassengerId() + "",driverJson);
            //解锁
            lock.unlock();
            return update;
        }
        return null;
    }

    @Override
    public ResponseResult testGetOrderById(Long id) {
        Order order = orderMapper.selectById(id);
        return ResponseResult.success(order);
    }

    @Override
    public ResponseResult toPickUpPassenger(OrderDto orderDto) {
        Long orderId = orderDto.getOrderId();
        Date toPickUpPassengerTime = orderDto.getToPickUpPassengerTime();
        String toPickUpPassengerAddress = orderDto.getToPickUpPassengerAddress();
        String toPickUpPassengerLongitude = orderDto.getToPickUpPassengerLongitude();
        String toPickUpPassengerLatitude = orderDto.getToPickUpPassengerLatitude();
        QueryWrapper<Order> wrapper = new QueryWrapper<>();
        wrapper.eq("id",orderId);
        Order order = orderMapper.selectOne(wrapper);
        order.setId(orderId);
        order.setToPickUpPassengerTime(toPickUpPassengerTime);
        order.setToPickUpPassengerAddress(toPickUpPassengerAddress);
        order.setToPickUpPassengerLongitude(toPickUpPassengerLongitude);
        order.setToPickUpPassengerLatitude(toPickUpPassengerLatitude);
        order.setOrderStatus(OrderConstant.TO_PICK_UP_STATUS);
        int update = orderMapper.updateById(order);
        if (update != 1){
            return ResponseResult.fail(CommonStatusEnum.FAIL_TO_UPDATE_ORDER_ERROR);
        }
        return ResponseResult.success();
    }

    @Override
    public ResponseResult driverArriveDeparture(OrderDto orderDto) {
        Long orderId = orderDto.getOrderId();
        QueryWrapper<Order> wrapper = new QueryWrapper<>();
        wrapper.eq("id",orderId);
        Order order = orderMapper.selectOne(wrapper);
        Date driverArrivedDepartureTime = orderDto.getDriverArrivedDepartureTime();
        order.setDriverArrivedDepartureTime(driverArrivedDepartureTime);
        order.setOrderStatus(OrderConstant.DRIVER_ARRIVE_DEPARTURE_STATUS);
        int update = orderMapper.updateById(order);
        if (update != 1){
            return ResponseResult.fail(CommonStatusEnum.FAIL_TO_UPDATE_ORDER_ERROR);
        }
        return ResponseResult.success();
    }

    @Override
    public ResponseResult pickUpPassenger(OrderDto orderDto) {
        Long orderId = orderDto.getOrderId();
        QueryWrapper<Order> wrapper = new QueryWrapper<>();
        wrapper.eq("id",orderId);
        Order order = orderMapper.selectOne(wrapper);
        String pickUpPassengerLongitude = orderDto.getPickUpPassengerLongitude();
        String pickUpPassengerLatitude = orderDto.getPickUpPassengerLatitude();
        Date pickUpPassengerTime = orderDto.getPickUpPassengerTime();
        order.setPickUpPassengerTime(pickUpPassengerTime);
        order.setPickUpPassengerLongitude(pickUpPassengerLongitude);
        order.setPickUpPassengerLatitude(pickUpPassengerLatitude);
        order.setOrderStatus(OrderConstant.PASSENGER_GETON_STATUS);
        int update = orderMapper.updateById(order);
        if (update != 1){
            return ResponseResult.fail(CommonStatusEnum.FAIL_TO_UPDATE_ORDER_ERROR);
        }
        return ResponseResult.success();
    }

    @Override
    public ResponseResult passengerGetoff(OrderDto orderDto) {
        Long orderId = orderDto.getOrderId();
        QueryWrapper<Order> wrapper = new QueryWrapper<>();
        wrapper.eq("id",orderId);
        Order order = orderMapper.selectOne(wrapper);
        Date passengerGetoffTime = orderDto.getPassengerGetoffTime();
        String passengerGetoffLongitude = orderDto.getPassengerGetoffLongitude();
        String passengerGetoffLatitude = orderDto.getPassengerGetoffLatitude();

        //driverMile、driverMile
        //查询当前车辆对应的终端id与轨迹id
        LinkedHashMap<String, Object> data = (LinkedHashMap<String, Object>) serviceDriverUserClient.selectCarById(order.getCarId()).getData();
        Integer tid = (Integer)data.get("tid");
        Integer trid = (Integer) data.get("trid");
        long startTime = order.getPickUpPassengerTime().getTime();
        long endTime = new Date().getTime();
        //查询乘客的乘客里程 与 乘客时间
        ResponseResult<TrsearchResponse> trsearch = serviceMapClient.trsearch(Long.parseLong(tid + ""), trid,startTime + "", endTime + "" );
        TrsearchResponse response = trsearch.getData();
        Long driverMile = response.getDriverMile();
        Long driverTime = (response.getDriverTime() / 1000);//毫秒 转换为 秒

        //根据计价规则计算乘客应付价格
        OrderPriceDto priceDto = new OrderPriceDto();
        priceDto.setDistance(driverMile);
        priceDto.setDuration(driverTime);
        priceDto.setCityCode(orderDto.getAddress());
        priceDto.setVehicleType(orderDto.getVehicleType());
        ResponseResult<BigDecimal> orderPriceResponse = servicePriceClient.calculateOrderPrice(priceDto);
        BigDecimal price = orderPriceResponse.getData();

        order.setDriverMile(driverMile);
        order.setDriverTime(driverTime);
        order.setPassengerGetoffTime(passengerGetoffTime);
        order.setPassengerGetoffLongitude(passengerGetoffLongitude);
        order.setPassengerGetoffLatitude(passengerGetoffLatitude);
        order.setPrice(price);
        order.setOrderStatus(OrderConstant.PASSENGER_GETOFF_STATUS);
        int update = orderMapper.updateById(order);
        if (update != 1){
            return ResponseResult.fail(CommonStatusEnum.FAIL_TO_UPDATE_ORDER_ERROR);
        }
        return ResponseResult.success();
    }

    @Override
    public ResponseResult finishOrderPay(String orderId) {

        QueryWrapper<Order> wrapper = new QueryWrapper<>();
        wrapper.eq("id",orderId);
        Order order = orderMapper.selectOne(wrapper);
        if (order == null){
            return ResponseResult.fail(CommonStatusEnum.ORDER_NOT_EXIST_ERROR);
        }
        order.setOrderStatus(OrderConstant.FINISH_PAYMENT_STATUS);
        int update = orderMapper.updateById(order);
        if (update != 1){
            return ResponseResult.fail(CommonStatusEnum.FAIL_TO_UPDATE_ORDER_ERROR);
        }
        return ResponseResult.success();
    }
}